package com.iiot.stream.tools;

import com.htiiot.common.db.DbConnectionPool;
import com.htiiot.common.db.JDBCUtils;
import com.htiiot.common.util.JedisMultiPool;
import org.apache.log4j.Logger;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;

import java.io.IOException;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 统计设备各状态的时长
 * 每分钟调度一次，从redis中获取设备上一次数据状态和时间进行时长统计
 * 只统计大于10分钟的
 * Created by dzm on 2018/6/12.
 */
public class DeviceStatusLengthStatistic extends TimerTask {
    private static Logger logger = Logger.getLogger(DeviceStatusLengthStatistic.class);
    private static SimpleDateFormat tf = new SimpleDateFormat("yyyy-MM-dd");
    @Override
    public void run(){
        logger.info("----一分钟到，启动遍历----");
        Long duration = Long.parseLong(Configs.RedisConfig.DURATION);
        int timeOut = Integer.parseInt(Configs.RedisConfig.TIMEOUT);
        // 获取 redis 连接
        Jedis redisHandle1 = JedisMultiPool.getJedis();
        Jedis redisHandle2 = JedisMultiPool.getJedis();
        Pipeline pl = redisHandle1.pipelined();
        DbConnectionPool pool = DbConnectionPool.getPool();
        Connection conn  = pool.getConnection();
        PreparedStatement psToStatus = null;
        PreparedStatement psToEvent = null;
        ResultSet rs = null;
        try{
            // 获取设备状态时间为非实时的设备id
            List<String> didList = getDevTimeDids(conn);
            psToStatus = conn.prepareStatement("UPDATE `thing_device` SET status = ? where id = ? AND data_type = 0 AND deleted = 0 AND type = 0 AND status != 0");
            psToEvent = conn.prepareStatement("INSERT INTO `thing_latest` " +
                                                "(create_time,tenant_id,device_id,message) " +
                                                "SELECT ?,?,?,CONCAT(`name`,?) " +
                                                "FROM `thing_device` WHERE id = ? AND deleted = 0 AND type = 0");


            // 获取所有设备的最新状态和时间
            Response<Map<String, String>> didAndTimeRes = pl.hgetAll("deviceStatus");
            pl.sync();
            Map<String,String> tiddidAllInfo = didAndTimeRes.get();
            // 去除设备状态时间为非实时的数据
            for(int i = 0;i < didList.size();i++){
                String tdid = didList.get(i);
                tiddidAllInfo.remove(tdid);
            }
            Iterator<String> iter = tiddidAllInfo.keySet().iterator();
            // 获取当前系统时间
            long currentTime = System.currentTimeMillis();
            // 批处理判断
            int batch = 0;
            // 遍历所有设备，进行时长统计
            while(iter.hasNext()){
                String tiddid = iter.next();
                String previousStatusAndTime = tiddidAllInfo.get(tiddid);
                String[] previousArr = previousStatusAndTime.split(":");
                String previousStatus = previousArr[0];
                long previousTime = Long.parseLong(previousArr[1]);
                // 间隔大于10分钟
                if(currentTime - previousTime > duration){
                    if(previousTime == Long.parseLong(redisHandle2.hget("deviceStatus",tiddid).split(":")[1])){
                        //设置设备当前状态和lastTime，状态修改为离线，时间修改为上次时间+10分钟
                        redisHandle2.hset("deviceStatus",tiddid,"off:" + (previousTime + duration));
                        // 零时时刻
                        long midNightTime = getCurrentMidNightMs(currentTime);
                        // 上一条数据到凌晨时刻的差
                        long lastToMidNight = previousTime - midNightTime;
                        // 上一条数据到凌晨时刻的差 + duration
                        long lastToMidNight2 = previousTime - midNightTime + duration;
                        // 今日累加的时长
                        long todayLength = 0L;
                        // 昨天累加的时长
                        long yesLength = 0L;
                        // 在同一天
                        if(lastToMidNight > 0){
                            todayLength = duration;
                        }else {
                            // 不在同一天
                            todayLength = lastToMidNight2 < 0 ? 0 : lastToMidNight2;
                            yesLength = lastToMidNight2 < 0 ? duration : duration - lastToMidNight2;
                        }
                        // 更新redis
                        String currentDate = TimeTransform.timestampToDate(currentTime);
                        String previousDate = TimeTransform.timestampToDate(previousTime);
                        // 更新昨日数据
                        if(lastToMidNight < 0){
                            pl.hincrBy("deviceStatus:" + previousStatus + ":" + previousDate, tiddid, yesLength);
                            pl.expire("deviceStatus:" + previousStatus + ":" + previousDate,timeOut);
                        }
                        // 更新设备上一状态时长
                        pl.hincrBy("deviceStatus:" + previousStatus + ":" + currentDate, tiddid, todayLength);
                        pl.expire("deviceStatus:" + previousStatus + ":" + currentDate, timeOut);
                        // 历史状态维护
                        pl.hincrBy("deviceStatusKeep:" + previousStatus, tiddid, duration);
                    }

                    // 如果检测时间和设备上一条数据时间间隔大于10分钟，将mysql中的状态置为off 0
                    if(!previousStatus.equals("off")){
                        // 添加事件
                        updateMysqlEvent(previousStatus,"off",currentTime,tiddid,psToEvent);
                        // 查询设备数据类型，类型为实时
                        // 更新 mysql 中设备状态为离线
                        updateMysqlStatus("off",tiddid,psToStatus);
                    }
                }
                // 提交批处理
                batch = batch + 1;
                if(batch >= 100){
                    batch = 0;
                    psToEvent.executeBatch();
                    psToStatus.executeBatch();
                    psToEvent.clearBatch();
                    psToStatus.clearBatch();
                }
            }

            psToEvent.executeBatch();
            psToStatus.executeBatch();
            psToEvent.clearBatch();
            psToStatus.clearBatch();
            logger.info("一分钟遍历用时---》" + (System.currentTimeMillis() - currentTime)/1000 + "s");
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            pl.sync();
            try {
                pl.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            redisHandle1.close();
            redisHandle2.close();
            JDBCUtils.close(psToStatus, rs, conn);
            JDBCUtils.close(psToEvent, null, null);
        }

    }

    // 获取当天时间凌晨对应的毫秒数
    public static long getCurrentMidNightMs(Long time){

        String timeData = tf.format(time);
        long midNightMs = time - (time % 86400000 + 28800000);
        if(tf.format(midNightMs).equals(timeData)){
            return midNightMs;
        }else {
            return midNightMs + 86400000;
        }
    }

    // 获取设备状态时间为数据产生时间的did
    public static List<String> getDevTimeDids(Connection conn){
        List<String> didList = new ArrayList<>();
        try{
            ResultSet dids = conn.createStatement().executeQuery("SELECT id,tenant_id FROM `thing_device` WHERE data_type = 1");
            while (dids.next()){
                didList.add(dids.getString(2) + ":" + dids.getString(1));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return didList;
    }

    // 查询mysql中的设备数据类型，已用sql解决
//    public static Object queryDidType(String tiddid,PreparedStatement psQueryDidType) throws SQLException{
//        // 获取设备编号
//        int did = Integer.parseInt(tiddid.split(":")[1]);
//        psQueryDidType.setInt(1,did);
//        ResultSet rs = psQueryDidType.executeQuery();
//        rs.next();
//        return rs.getObject(1);
//    }

    // 更新mysql中的设备状态
    public static void updateMysqlStatus(String toMysqlStatus,String tiddid,PreparedStatement psToStatus) throws SQLException{
        int sta = 0;
        if (toMysqlStatus.equals("run")) sta = 16;
        else if (toMysqlStatus.equals("standby")) sta = 64;
        else if (toMysqlStatus.equals("fault"))sta = 32;
        // 获取设备编号
        int did = Integer.parseInt(tiddid.split(":")[1]);
        psToStatus.setInt(1,sta);
        psToStatus.setInt(2,did);
        psToStatus.addBatch();
    }

    // 更新mysql中的event事件
    public static void updateMysqlEvent(String previousStatus, String currentStatus,Long currentTime,String tiddid,PreparedStatement psToEvent)throws SQLException{
        int tanentId = Integer.parseInt(tiddid.split(":")[0]);
        int deviceId = Integer.parseInt(tiddid.split(":")[1]);
        // 非故障状态转故障状态
        if(!previousStatus.equals("fault") && currentStatus.equals("fault")){
            // 如果两次数据的状态不一样，更新mysql事件表中的事件
            psToEvent.setTimestamp(1,new Timestamp(currentTime));
            psToEvent.setInt(2,tanentId);
            psToEvent.setInt(3,deviceId);
            psToEvent.setString(4,"发生故障");
            psToEvent.setInt(5,deviceId);
            psToEvent.addBatch();
        }
        // 故障状态转非故障状态
        if(previousStatus.equals("fault") && !currentStatus.equals("fault")){
            // 如果两次数据的状态不一样，更新mysql事件表中的事件
            psToEvent.setTimestamp(1,new Timestamp(currentTime));
            psToEvent.setInt(2,tanentId);
            psToEvent.setInt(3,deviceId);
            psToEvent.setString(4,"恢复正常");
            psToEvent.setInt(5,deviceId);
            psToEvent.addBatch();
        }
        // 非离线状态转离线状态
        if(!previousStatus.equals("off") && currentStatus.equals("off")){
            // 如果两次数据的状态不一样，更新mysql事件表中的事件
            psToEvent.setTimestamp(1,new Timestamp(currentTime));
            psToEvent.setInt(2,tanentId);
            psToEvent.setInt(3,deviceId);
            psToEvent.setString(4,"已离线");
            psToEvent.setInt(5,deviceId);
            psToEvent.addBatch();
        }
        // 离线状态转非离线状态
        if(previousStatus.equals("off") && !currentStatus.equals("off")){
            // 如果两次数据的状态不一样，更新mysql事件表中的事件
            psToEvent.setTimestamp(1,new Timestamp(currentTime));
            psToEvent.setInt(2,tanentId);
            psToEvent.setInt(3,deviceId);
            psToEvent.setString(4,"已上线");
            psToEvent.setInt(5,deviceId);
            psToEvent.addBatch();
        }
    }

}




























